import * as d3 from 'd3'
import * as d3Sankey from 'd3-sankey'
export class Sankey {
  divId = ''
  data = []
  chart = {}
  layout = 'sankeyJustify'
  color = d3.scaleOrdinal(d3.schemeCategory10)
  constructor(divId) {
    this.divId = divId
  }
  setData(data) {
    this.data = data
  }
  draw() {
    this.initChart()
    this.initLayout()
    this.drawSankey()
  }

  initChart() {
    this.chart.padding = {
      left: 10,
      right: 10,
      top: 10,
      bottom: 10
    }
    this.chart.divHeight = document.getElementById(this.divId).offsetHeight -
        this.chart.padding.top -
        this.chart.padding.bottom
    this.chart.divWidth = document.getElementById(this.divId).offsetWidth -
        this.chart.padding.left -
        this.chart.padding.right

    this.chart.svg = d3
      .select('#' + this.divId)
      .append('svg')
      .attr('width', this.chart.divWidth +
          this.chart.padding.left +
          this.chart.padding.right
      )
      .attr('height', this.chart.divHeight +
          this.chart.padding.top +
          this.chart.padding.bottom
      )
      .attr('class', 'svg_chart')
  }

  changeLayout() {
    this.updateSankey()
  }

  initLayout() {
    this.chart.sankey = d3Sankey.sankey()
      //   .nodeId(d => d.name)
      .nodeWidth(15)
      .nodePadding(10)
      .extent([
        [this.chart.padding.left, this.chart.padding.top],
        [this.chart.divWidth, this.chart.divHeight]
      ])

    this.chart.node = this.chart.svg.append('g')
      .attr('class', 'nodes')
      .style('font-family', 'sans-serif')
      .style('font-size', 10)

    this.chart.link = this.chart.svg.append('g')
      .attr('class', 'links')
      .style('fill', 'none')
      .style('stroke', '#000')
      .style('stroke-opacity', 0.2)
  }

  drawSankey() {
    // let drag = d3.drag()
    //   .subject(function (d) {
    //     return d
    //   })
    //   .on('start', function () {
    //     this.parentNode.appendChild(this)
    //   })
    //   .on('drag', function (d) {
    //     d3.select(this)
    //       .attr('transform', 'translate(' + 0 + ',' + d3.event.y + ')')

    //   })
    let svg = this.chart.svg
    this.chart.sankey.nodeAlign(d3Sankey[this.layout])
    console.log(this.data)
    let sankeData = this.chart.sankey(this.data)
    let link = this.chart.link.selectAll('.link')
      .data(sankeData.links)

    let linkEnter = link.enter()
      .append('g')
      .attr('class', 'link')
      .on('mouseover', linkMouseOver)
      .on('mouseout', mouseOut)

    linkEnter.append('path')
      .attr('d', d3Sankey.sankeyLinkHorizontal())
      .style('stroke-dasharray', function(d) {
        return this.getTotalLength()
      })
      .style('stroke-dashoffset', function(d) {
        return this.getTotalLength()
      })
      .style('stroke', 'red')
      .style('stroke-width', d => Math.max(4, d.width))

    let linkUpdate = linkEnter.merge(link)

    linkUpdate.select('path')
      .transition()
      .duration(1000)
      .style('stroke-dashoffset', 0)

    link.exit().remove()

    let node = this.chart.node.selectAll('.node')
      .data(sankeData.nodes)

    let nodeEnter = node.enter()
      .append('g')
      .attr('class', 'node')
      .on('mouseover', nodeMouseOver)
      .on('mouseout', mouseOut)
      // .call(drag)

    nodeEnter.append('rect')
      .attr('width', 0)
      .attr('height', d => {
        return d.y1 - d.y0
      })
      .attr('rx', 2)
      .attr('ry', 2)
      .style('fill', (d, i) => d.color || this.color(i))
      .attr('stroke', '#000')
      .attr('x', d => d.x0)
      .attr('y', d => d.y0)

    nodeEnter.append('text')
      .attr('x', d => d.x1 > this.chart.divWidth / 2 ? (d.x0 - 5 - d.name.length * 5) : d.x1 + 5)
      .attr('y', d => (d.y1 - d.y0) / 2 + d.y0)
      .text(d => d.name)

    let nodeUpdate = nodeEnter.merge(node)

    nodeUpdate.select('rect')
      .transition()
      .duration(750)
      .attr('width', d => d.x1 - d.x0)

    nodeUpdate.select('text')
      .transition()
      .duration(750)
      .attr('x', d => d.x1 > this.chart.divWidth / 2 ? (d.x0 - 5 - d.name.length * 5) : d.x1 + 5)
      .attr('y', d => (d.y1 - d.y0) / 2 + d.y0)

    node.exit().remove()

    function nodeMouseOver(d) {
      svg.select('.nodes').selectAll('.node')
        .filter(o => {
          let flag = false
          for (let i = 0; i < d.sourceLinks.length; ++i) {
            if (o === d.sourceLinks[i].target) {
              flag = true
            }
          }
          for (let i = 0; i < d.targetLinks.length; ++i) {
            if (o === d.targetLinks[i].source) {
              flag = true
            }
          }
          if (d.index === o.index) flag = true
          if (flag === false) return o
        })
        .style('opacity', 0.2)

      svg.select('.links').selectAll('.link')
        .filter(o => {
          return (d.sourceLinks.indexOf(o) === -1) && (d.targetLinks.indexOf(o) === -1)
        })
        .style('opacity', 0.3)
    }

    function mouseOut(d) {
      svg.select('.nodes').selectAll('.node')
        .style('opacity', 1)

      svg.select('.links').selectAll('.link')
        .style('opacity', 1)
    }

    function linkMouseOver(d) {
      svg.select('.nodes').selectAll('.node')
        .filter(o => {
          return o !== d.target && o !== d.source
        })
        .style('opacity', 0.2)

      svg.select('.links').selectAll('.link')
        .filter(o => {
          return d !== o
        })
        .style('opacity', 0.3)
    }
  }

  updateSankey() {
    this.chart.sankey.nodeAlign(d3Sankey[this.layout])

    let sankeData = this.chart.sankey(this.data)

    this.chart.sankey.update(sankeData)

    this.chart.link.selectAll('.link')
      .data(sankeData.links)
      .select('path')
      .transition()
      .duration(750)
      .attr('d', d3Sankey.sankeyLinkHorizontal())

    let nodes = this.chart.node.selectAll('.node')
      .data(sankeData.nodes)

    nodes.selectAll('rect')
      .transition()
      .duration(750)
      .attr('x', d => d.x0)
      .attr('y', d => d.y0)

    nodes.selectAll('text')
      .transition()
      .duration(750)
      .attr('x', d => d.x1 > this.chart.divWidth / 2 ? (d.x0 - 5 - d.name.length * 5) : d.x1 + 5)
      .attr('y', d => (d.y1 - d.y0) / 2 + d.y0)
  }
}
